
#include "pgm.h"
#include "bitswap.h"

static unsigned short *sharedprotram;

int pstarsScan(int nAction, int *);
int killbldScan(int nAction, int *);
int drgw3Scan(int nAction, int *);
int kov_asic27Scan(int nAction, int *);
int asic3Scan(int nAction, int *);
int asic27AScan(int nAction, int *);
int oldsScan(int nAction, int *);
int kovsh_asic27aScan(int nAction, int *);
int ddp3Scan(int nAction, int *);
int svg_asic27aScan(int nAction,int *);

//-----------------------------------------------------------------------------------------------------
// ASIC3 - Oriental Legends

static unsigned char asic3_reg, asic3_latch[3], asic3_x, asic3_y, asic3_z, asic3_h1, asic3_h2;
static unsigned short asic3_hold;

static unsigned int bt(unsigned int v, int bit)
{
	return (v & (1<<bit)) != 0;
}

static void asic3_compute_hold()
{
	// The mode is dependant on the region
	static int modes[4] = { 1, 1, 3, 2 };
	int mode;
	if ((strcmp(BurnDrvGetTextA(DRV_NAME), "orlegend111c") == 0) ||
			(strcmp(BurnDrvGetTextA(DRV_NAME), "orlegendca") == 0))
		mode = modes[PgmInput[7] & 3];
	else
		mode = modes[2 & 3];

	switch(mode) {
	case 1:
		asic3_hold =
			(asic3_hold << 1)
			^0x2bad
			^bt(asic3_hold, 15)^bt(asic3_hold, 10)^bt(asic3_hold, 8)^bt(asic3_hold, 5)
			^bt(asic3_z, asic3_y)
			^(bt(asic3_x, 0) << 1)^(bt(asic3_x, 1) << 6)^(bt(asic3_x, 2) << 10)^(bt(asic3_x, 3) << 14);
		break;
	case 2:
		asic3_hold =
			(asic3_hold << 1)
			^0x2bad
			^bt(asic3_hold, 15)^bt(asic3_hold, 7)^bt(asic3_hold, 6)^bt(asic3_hold, 5)
			^bt(asic3_z, asic3_y)
			^(bt(asic3_x, 0) << 4)^(bt(asic3_x, 1) << 6)^(bt(asic3_x, 2) << 10)^(bt(asic3_x, 3) << 12);
		break;
	case 3:
		asic3_hold =
			(asic3_hold << 1)
			^0x2bad
			^bt(asic3_hold, 15)^bt(asic3_hold, 10)^bt(asic3_hold, 8)^bt(asic3_hold, 5)
			^bt(asic3_z, asic3_y)
			^(bt(asic3_x, 0) << 4)^(bt(asic3_x, 1) << 6)^(bt(asic3_x, 2) << 10)^(bt(asic3_x, 3) << 12);
		break;
	}
}

static unsigned char pgm_asic3_r()
{
	unsigned char res = 0;
	/* region is supplied by the protection device */
	switch(asic3_reg) {
	case 0x00:
		{
			if ((strcmp(BurnDrvGetTextA(DRV_NAME), "orlegend111c") == 0) ||
					(strcmp(BurnDrvGetTextA(DRV_NAME), "orlegendca") == 0))
				res = (asic3_latch[0] & 0xf7) | ((PgmInput[7] << 3) & 0x08);
			else
				res = (asic3_latch[0] & 0xf7) | ((2 << 3) & 0x08);
		}
		break;
	case 0x01: res = asic3_latch[1]; break;
	case 0x02:
		{
			if ((strcmp(BurnDrvGetTextA(DRV_NAME), "orlegend111c") == 0) ||
					(strcmp(BurnDrvGetTextA(DRV_NAME), "orlegendca") == 0))
				res = (asic3_latch[2] & 0x7f) | ((PgmInput[7] << 6) & 0x80);
			else
				res = (asic3_latch[2] & 0x7f) | ((2 << 6) & 0x80);
		}
		break;
	case 0x03:
		res = (bt(asic3_hold, 15) << 0)
			| (bt(asic3_hold, 12) << 1)
			| (bt(asic3_hold, 13) << 2)
			| (bt(asic3_hold, 10) << 3)
			| (bt(asic3_hold,  7) << 4)
			| (bt(asic3_hold,  9) << 5)
			| (bt(asic3_hold,  2) << 6)
			| (bt(asic3_hold,  5) << 7);
		break;
	case 0x20: res = 0x49; break;
	case 0x21: res = 0x47; break;
	case 0x22: res = 0x53; break;
	case 0x24: res = 0x41; break;
	case 0x25: res = 0x41; break;
	case 0x26: res = 0x7f; break;
	case 0x27: res = 0x41; break;
	case 0x28: res = 0x41; break;
	case 0x2a: res = 0x3e; break;
	case 0x2b: res = 0x41; break;
	case 0x2c: res = 0x49; break;
	case 0x2d: res = 0xf9; break;
	case 0x2e: res = 0x0a; break;
	case 0x30: res = 0x26; break;
	case 0x31: res = 0x49; break;
	case 0x32: res = 0x49; break;
	case 0x33: res = 0x49; break;
	case 0x34: res = 0x32; break;
	}
	return res;
}

static void pgm_asic3_w(unsigned short data)
{
	{
		if (asic3_reg < 3)
			asic3_latch[asic3_reg] = data << 1;
		else if (asic3_reg == 0xa0) {
			asic3_hold = 0;
		} else if (asic3_reg == 0x40) {
			asic3_h2 = asic3_h1;
			asic3_h1 = data;
		} else if (asic3_reg == 0x48) {
			asic3_x = 0;
			if(!(asic3_h2 & 0x0a)) asic3_x |= 8;
			if(!(asic3_h2 & 0x90)) asic3_x |= 4;
			if(!(asic3_h1 & 0x06)) asic3_x |= 2;
			if(!(asic3_h1 & 0x90)) asic3_x |= 1;
		} else if (asic3_reg >= 0x80 && asic3_reg <= 0x87) {
			asic3_y = asic3_reg & 7;
			asic3_z = data;
			asic3_compute_hold();
		}
	}
}

static void pgm_asic3_reg_w(unsigned short data)
{
	asic3_reg = data & 0xff;
}

static void __fastcall asic3_write_word(unsigned int address, unsigned short data)
{
	if (address == 0xc04000) {
		pgm_asic3_reg_w(data);
		return;
	}

	if (address == 0xc0400e) {
		pgm_asic3_w(data);
		return;
	}
}

static unsigned short __fastcall asic3_read_word(unsigned int address)
{
	if (address == 0xc0400e) {
		return pgm_asic3_r();
	}

	return 0;
}

void reset_asic3()
{
	asic3_latch[0] = asic3_latch[1] = asic3_latch[2] = 0;
	asic3_hold = asic3_reg = asic3_x = asic3_y, asic3_z = asic3_h1 = asic3_h2 = 0;
}

void install_protection_asic3()
{
	pPgmScanCallback = asic3Scan;

	SekOpen(0);
	SekMapHandler(4,	0xc04000, 0xc0400f, SM_READ | SM_WRITE);

	SekSetReadWordHandler(4, asic3_read_word);
	SekSetWriteWordHandler(4, asic3_write_word);
	SekClose();
}


//-----------------------------------------------------------------------------------------------------
// ASIC27 - Knights of Valour

//Not sure if BATABLE is complete
static const unsigned int BATABLE[0x40] = {
		0x00,0x29,0x2c,0x35,0x3a,0x41,0x4a,0x4e,	//0x00
		0x57,0x5e,0x77,0x79,0x7a,0x7b,0x7c,0x7d,	//0x08
		0x7e,0x7f,0x80,0x81,0x82,0x85,0x86,0x87,	//0x10
		0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x90,	//0x18
		0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,
		0x9e,0xa3,0xd4,0xa9,0xaf,0xb5,0xbb,0xc1 };

static const unsigned int B0TABLE[16] = { 2, 0, 1, 4, 3 }; //maps char portraits to tables

static unsigned short ASIC27KEY;
static unsigned short ASIC27REGS[10];
static unsigned short ASICPARAMS[256];
static unsigned short ASIC27RCNT = 0;
static unsigned int E0REGS[16];

static unsigned short kov_asic27_protram_r(unsigned short offset)
{
	if (offset == 8) return PgmInput[7];

	return 0x0000;
}
static unsigned short pgm_kov_asic27_r(unsigned short offset)
{
	unsigned int val=(ASIC27REGS[1]<<16)|(ASIC27REGS[0]);

	switch (ASIC27REGS[1] & 0xff) {
		case 0x99:
			val = 0x880000;
			break;
		case 0x9d:	// spr palette
			val = 0xa00000 + ((ASIC27REGS[0] & 0x1f) << 6);
			break;
		case 0xb0:
			val = B0TABLE[ASIC27REGS[0] & 0xf];
			break;
		case 0xb4:
			{
				int v2 = ASIC27REGS[0] & 0x0f;
				int v1 = (ASIC27REGS[0]&0x0f00) >> 8;
				if (ASIC27REGS[0] == 0x102)
					E0REGS[1] = E0REGS[0];
				else
					E0REGS[v1]=E0REGS[v2];
				val = 0x880000;
			}
			break;
		case 0xba:
			val = BATABLE[ASIC27REGS[0]&0x3f];
			if (ASIC27REGS[0] > 0x2f) {

			}
			break;
		case 0xc0:
			val = 0x880000;
			break;
		case 0xc3:	//TXT tile position Uses C0 to select column
			val = 0x904000 + (ASICPARAMS[0xc0] + ASICPARAMS[0xc3] * 64) * 4;
			break;
		case 0xcb:
			val = 0x880000;
			break;
		case 0xcc:	//BG
			{
	   	 		int y = ASICPARAMS[0xcc];
	    		if (y & 0x400)    //y is signed (probably x too and it also applies to TXT, but I've never seen it used)
	     			y = -(0x400-(y&0x3ff));
	    		val = 0x900000+(((ASICPARAMS[0xcb]+(y)*64)*4)/*&0x1fff*/);
   			}
   			break;
		case 0xd0:	//txt palette
			val = 0xa01000 + (ASIC27REGS[0] << 5);
			break;
		case 0xd6:	//???? check it
			{
				int v2 = ASIC27REGS[0] & 0xf;
				E0REGS[0] = E0REGS[v2];
				//E0REGS[v2] = 0;
				val = 0x880000;
			}
			break;
		case 0xdc:	//bg palette
			val = 0xa00800 + (ASIC27REGS[0]<<6);
			break;
		case 0xe0:	//spr palette
			val = 0xa00000 + ((ASIC27REGS[0]&0x1f)<<6);
			break;
		case 0xe5:
			val = 0x880000;
			break;
		case 0xe7:
			val = 0x880000;
			break;
		case 0xf0:
			val = 0x00C000;
			break;
		case 0xf8:
			val = E0REGS[ASIC27REGS[0]&0xf]&0xffffff;
			break;
		case 0xfc:	//Adjust damage level to char experience level
			val = (ASICPARAMS[0xfc]*ASICPARAMS[0xfe])>>6;
			break;
		case 0xfe:	//todo
			val = 0x880000;
			break;
		default:
			val = 0x880000;
	}

	if (offset == 0) {
		unsigned short d = val & 0xffff;
		unsigned short realkey = ASIC27KEY >> 8;
		realkey |= ASIC27KEY;
		d ^= realkey;
		return d;
	}
	else if (offset == 2) {
		unsigned short d = val >> 16;
		unsigned short realkey = ASIC27KEY >> 8;
		realkey |= ASIC27KEY;
		d ^= realkey;
		ASIC27RCNT++;
		if (!(ASIC27RCNT & 0xf)) {
			ASIC27KEY += 0x100;
			ASIC27KEY &= 0xFF00;
		}
		return d;
	}
	return 0xff;
}

static void pgm_kov_asic27_w(unsigned short offset, unsigned short data)
{
	if (offset == 0) {
		unsigned short realkey;
		realkey = ASIC27KEY>>8;
		realkey |= ASIC27KEY;
		data ^= realkey;
		ASIC27REGS[0] = data;
		return;
	}
	if (offset == 2) {
		unsigned short realkey;

		ASIC27KEY = data & 0xff00;

		realkey = ASIC27KEY >> 8;
		realkey |= ASIC27KEY;
		data ^= realkey;
		ASIC27REGS[1] = data;

		ASICPARAMS[ASIC27REGS[1] & 0xff] = ASIC27REGS[0];
		if (ASIC27REGS[1] == 0xE7) {
			unsigned int E0R = (ASICPARAMS[0xE7] >> 12) & 0xf;
			E0REGS[E0R] &= 0xffff;
			E0REGS[E0R] |= ASIC27REGS[0] << 16;
		}
		if (ASIC27REGS[1] == 0xE5) {
			unsigned int E0R=(ASICPARAMS[0xE7]>>12)&0xf;
			E0REGS[E0R] &= 0xff0000;
			E0REGS[E0R] |= ASIC27REGS[0];
		}
		ASIC27RCNT = 0;
	}
}

static void __fastcall kov_asic27_write_byte(unsigned int address, unsigned char data)
{
	if ((address & 0xfffffc) == 0x500000) {
		pgm_kov_asic27_w(address & 3, data);
	}
}

static void __fastcall kov_asic27_write_word(unsigned int address, unsigned short data)
{
	if ((address & 0xfffffc) == 0x500000) {
		pgm_kov_asic27_w(address & 3, data);
	}
}

static unsigned char __fastcall kov_asic27_read_byte(unsigned int address)
{
	if ((address & 0xff0000) == 0x4f0000) {
		return kov_asic27_protram_r(address & 0xffff);
	}

	if ((address & 0xfffffc) == 0x500000) {
		return pgm_kov_asic27_r(address & 3);
	}

	return 0;
}

static unsigned short __fastcall kov_asic27_read_word(unsigned int address)
{
	if ((address & 0xff0000) == 0x4f0000) {
		return kov_asic27_protram_r(address & 0xffff);
	}

	if ((address & 0xfffffc) == 0x500000) {
		return pgm_kov_asic27_r(address & 3);
	}

	return 0;
}

void reset_asic27()
{
	ASIC27KEY = ASIC27RCNT = 0;
	memset(ASIC27REGS, 0, 10);
	memset(ASICPARAMS, 0, 256);
	memset(E0REGS, 0, 16);
}

void install_protection_kov()
{
	pPgmScanCallback = kov_asic27Scan;

	SekOpen(0);
	SekMapHandler(4,	0x4f0000, 0x500003, SM_READ | SM_WRITE);

	SekSetReadWordHandler(4, kov_asic27_read_word);
	SekSetReadByteHandler(4, kov_asic27_read_byte);
	SekSetWriteWordHandler(4, kov_asic27_write_word);
	SekSetWriteByteHandler(4, kov_asic27_write_byte);
	SekClose();
}


//-----------------------------------------------------------------------------------------------------
// Dragon World 2

#define DW2BITSWAP(s,d,bs,bd)  d=((d&(~(1<<bd)))|(((s>>bs)&1)<<bd))

static unsigned short __fastcall dw2_read_word(unsigned int)
{
	// The value at 0x80EECE is computed in the routine at 0x107c18

	unsigned short d = SekReadWord(0x80EECE);
	unsigned short d2 = 0;

	d=(d>>8)|(d<<8);
	DW2BITSWAP(d,d2,7 ,0);
	DW2BITSWAP(d,d2,4 ,1);
	DW2BITSWAP(d,d2,5 ,2);
	DW2BITSWAP(d,d2,2 ,3);
	DW2BITSWAP(d,d2,15,4);
	DW2BITSWAP(d,d2,1 ,5);
	DW2BITSWAP(d,d2,10,6);
	DW2BITSWAP(d,d2,13,7);

	// ... missing bitswaps here (8-15) there is not enough data to know them
	// the code only checks the lowest 8 bits

	return d2;
}

void install_protection_dw2()
{
	SekOpen(0);
	SekMapHandler(4,	0xd80000, 0xd80003, SM_READ);
	SekSetReadWordHandler(4, dw2_read_word);
	SekClose();
}


//-----------------------------------------------------------------------------------------------------
// IGS022 - The Killing Blade

static unsigned int kb_regs[0x10];
static unsigned short kb_cmd, kb_reg, kb_ptr, kb_region_sequence_position;

static void IGS022_do_dma(unsigned short src, unsigned short dst, unsigned short size, unsigned short mode)
{
	unsigned short param;
	unsigned short *PROTROM = (unsigned short *)PGMUSER1;

	param = mode >> 8;
	mode &=0xf;  // what are the other bits?

	switch (mode)
	{
		case 0:
		case 1:
		case 2:
		case 3:
		{
			for (int x = 0; x < size; x++)
			{
				unsigned short dat2 = PROTROM[src + x];

				unsigned char extraoffset = param&0xfe; // the lowest bit changed the table addressing in tests. it's still related to the main one, not identical
				unsigned char *dectable = (unsigned char *)PROTROM;//rawDataEven; // the basic decryption table is at the start of the mcu data rom! at least in killbld
				unsigned short extraxor = ((dectable[((x*2)+0+extraoffset)&0xff]) << 8) | (dectable[((x*2)+1+extraoffset)&0xff] << 0);

				dat2 = ((dat2 & 0x00ff)<<8) | ((dat2 & 0xff00)>>8);

				//  mode==0 plain
				if (mode==3) dat2 ^= extraxor;
				if (mode==2) dat2 += extraxor;
				if (mode==1) dat2 -= extraxor;

				sharedprotram[dst + x] = dat2;
			}

			// different region IGS025 devices supply different sequences - we currently only have the china sequence for Killing Blade
			if ((mode==3) && (param==0x54) && (src*2==0x2120) && (dst*2==0x2600))
				sharedprotram[0x2600 / 2] = 0x4e75;

		}
		break;

		case 5: // copy
		{
			for (int x = 0; x < size; x++)
			{
				sharedprotram[dst + x] = PROTROM[src + x];
			}
		}
		break;

		case 6: // swap bytes and nibbles
		{
			for (int x = 0; x < size; x++)
			{
				unsigned short dat = PROTROM[src + x];

				dat = ((dat & 0xf000) >> 12)|
					  ((dat & 0x0f00) >> 4)|
					  ((dat & 0x00f0) << 4)|
					  ((dat & 0x000f) << 12);

				sharedprotram[dst + x] = dat;
			}
		}
		break;

		case 4: // fixed value xor?
		case 7: // params left in memory? nop?
		default: // ?
		break;
	}
}

static void IGS022_reset()
{
	int i;
	unsigned short *PROTROM = (unsigned short *)PGMUSER1;
	unsigned short tmp;

	sharedprotram = (unsigned short*)PGMUSER0;

	// fill ram with A5 patern
	for (i = 0; i < 0x4000/2; i++)
		sharedprotram[i] = 0xa55a;

	// the auto-dma
	unsigned short src = PROTROM[0x100 / 2];
	unsigned int dst = PROTROM[0x102 / 2];
	unsigned short size = PROTROM[0x104/ 2];
	unsigned short mode = PROTROM[0x106 / 2];

	src = ((src & 0xff00) >> 8) | ((src & 0x00ff) << 8);
	dst = ((dst & 0xff00) >> 8) | ((dst & 0x00ff) << 8);
	size = ((size & 0xff00) >> 8) | ((size & 0x00ff) << 8);
	mode &= 0xff;

	src >>= 1;

	IGS022_do_dma(src, dst, size, mode);

	// there is also a version ID? (or is it some kind of checksum) that is stored in the data rom, and gets copied..
	// Dragon World 3 checks it
	tmp = PROTROM[0x114/2];
	tmp = ((tmp & 0xff00) >> 8) | ((tmp & 0x00ff) << 8);
	sharedprotram[0x2a2/2] = tmp;
}

static void IGS022_handle_command()
{
	unsigned short cmd = sharedprotram[0x200/2];

	if (cmd == 0x6d)	//Store values to asic ram
	{
		unsigned int p1 = (sharedprotram[0x298/2] << 16) | sharedprotram[0x29a/2];
		unsigned int p2 = (sharedprotram[0x29c/2] << 16) | sharedprotram[0x29e/2];

		if ((p2 & 0xffff) == 0x9)	//Set value
		{
			int reg = (p2 >> 16) & 0xffff;
			if (reg & 0x200)
				kb_regs[reg & 0xff] = p1;
		}
		if ((p2 & 0xffff) == 0x6)	//Add value
		{
			int src1 = (p1 >> 16) & 0xff;
			int src2 = (p1 >> 0 ) & 0xff;
			int dst  = (p2 >> 16) & 0xff;
			kb_regs[dst] = kb_regs[src2] - kb_regs[src1];
		}
		if ((p2 & 0xffff) == 0x1)	//Add Imm?
		{
			int reg = (p2 >> 16) & 0x00ff;
			int imm = (p1 >> 0 ) & 0xffff;
			kb_regs[reg] += imm;
		}
		if ((p2 & 0xffff) == 0xa)	//Get value
		{
			int reg = (p1 >> 16) & 0xFF;
			sharedprotram[0x29c/2] = (kb_regs[reg] >> 16) & 0xffff;
			sharedprotram[0x29e/2] = kb_regs[reg] & 0xffff;
		}
	}
	if(cmd == 0x4f)	//memcpy with encryption / scrambling
	{
		unsigned short src  = sharedprotram[0x290 / 2] >> 1; // ?
		unsigned int   dst  = sharedprotram[0x292 / 2];
		unsigned short size = sharedprotram[0x294 / 2];
		unsigned short mode = sharedprotram[0x296 / 2];

		IGS022_do_dma(src,dst,size,mode);
	}
}

static void killbld_prot_w(unsigned int offset, unsigned short data)
{
	offset &= 0xf;

	if (offset == 0) {
		kb_cmd = data;
		return;
	}

	switch (kb_cmd)
	{
		case 0:
			kb_reg = data;
		break;

		case 2:
			if (data == 1) {
				IGS022_handle_command();
				kb_reg++;
			}
		break;

		case 4:
			kb_ptr = data;
		break;

		case 0x20:
			kb_ptr++;
		break;

		default:
			break;
	}
}

static unsigned short killbld_prot_r(unsigned int offset)
{
	offset &= 0xf;

	unsigned short res = 0;

	if (offset == 1)
	{
		if (kb_cmd == 1)
		{
			res = kb_reg & 0x7f;
		}
		else if (kb_cmd == 5)
		{
#if 1
			unsigned char kb_region_sequence[11] = {0x17, 0x14, 0x91, 0x89, 0x21, 0xD5, 0x7C, 0x65, 0x8F, 0x8E, 0xE1};
			unsigned char ret;

			// this isn't properly understood.. should be some kind of bitswap / xor / shift..based on values written to 0x22/0x23 etc.?
			// return hardcoded china sequence results for now, avoids rom patch
			if (kb_region_sequence_position < 11)
			{
				ret = kb_region_sequence[kb_region_sequence_position];
				kb_region_sequence_position++;
			}
			else
			{
				unsigned int protvalue = 0x89911400 | PgmInput[7];
				ret = (protvalue >> (8 * (kb_ptr - 1))) & 0xff;
			}
			res = 0x3f00 | ret;  // always 0x3fxx in logged behavior...
#else
			unsigned int protvalue = 0x89911400 | PgmInput[7];
			res = (protvalue >> (8 * (kb_ptr - 1))) & 0xff;
#endif
		}
	}

	return res;
}

static void __fastcall killbld_write_word(unsigned int address, unsigned short data)
{
	killbld_prot_w(address / 2, data);
}

static unsigned short __fastcall killbld_read_word(unsigned int address)
{
	return killbld_prot_r(address / 2);
}

void reset_killbld()
{
	kb_cmd = kb_reg = kb_ptr = kb_region_sequence_position = 0;
	memset(kb_regs, 0, 0x10);

	IGS022_reset();
}

void install_protection_killbld()
{
	pPgmScanCallback = killbldScan;

	SekOpen(0);
	SekMapMemory(PGMUSER0,	0x300000, 0x303fff, SM_RAM);
	SekMapHandler(4,	0xd40000, 0xd40003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, killbld_read_word);
	SekSetWriteWordHandler(4, killbld_write_word);
	SekClose();
}


//-----------------------------------------------------------------------------------------------------
// IGS022 - Dragon World 3

#define DW3BITSWAP(s,d,bs,bd)  d=((d&(~(1<<bd)))|(((s>>bs)&1)<<bd))
static unsigned char dw3_swap;

static void drgw3_prot_w(unsigned int offset, unsigned short data)
{
	offset &= 0xf;

	if (offset == 0)
		kb_cmd = data;
	else //offset==2
	{
		if (kb_cmd == 0)
			kb_reg = data;
		else if (kb_cmd == 3)
			dw3_swap = data;
		else if (kb_cmd == 0x20)
			kb_ptr++;
	}
}

static unsigned short drgw3_prot_r(unsigned int offset)
{
	offset &= 0xf;

	unsigned short res = 0;

	if (offset == 1)
	{
		if (kb_cmd == 0)	//swap
		{
				unsigned char v1 = (dw3_swap+1)&0x7F;
				unsigned char v2 = 0;
				DW3BITSWAP(v1,v2,7,0);
				DW3BITSWAP(v1,v2,6,1);
				DW3BITSWAP(v1,v2,5,2);
				DW3BITSWAP(v1,v2,4,3);
				DW3BITSWAP(v1,v2,3,4);
				DW3BITSWAP(v1,v2,2,5);
				DW3BITSWAP(v1,v2,1,6);
				DW3BITSWAP(v1,v2,0,7);

				res=v2;
		}
		else if (kb_cmd == 1)
		{
			res = kb_reg & 0x7f;
		}
		else if (kb_cmd == 5)
		{
			unsigned int protvalue = 0x60000 | PgmInput[7];
			res = (protvalue >> (8 * (kb_ptr - 1))) & 0xff;
		}
	}

	return res;
}

static void __fastcall drgw3_write_word(unsigned int address, unsigned short data)
{
	drgw3_prot_w(address / 2, data);
}

static unsigned short __fastcall drgw3_read_word(unsigned int address)
{
	return drgw3_prot_r(address / 2);
}

void reset_drgw3()
{
	kb_cmd = kb_reg = kb_ptr = dw3_swap = 0;

	IGS022_reset();
	sharedprotram[0x202/2] = 0x007c; // it cares about this, operation status flag?
}

void install_protection_drgw3()
{
	pPgmScanCallback = drgw3Scan;

	SekOpen(0);
	SekMapMemory(PGMUSER0,	0x300000, 0x303fff, SM_RAM);
	SekMapHandler(4,	0xda5610, 0xda5613, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, drgw3_read_word);
	SekSetWriteWordHandler(4, drgw3_write_word);
	SekClose();
}


//-----------------------------------------------------------------------------------------------------
// PStars

unsigned short PSTARSKEY;
static unsigned short PSTARSINT[2];
static unsigned int PSTARS_REGS[16];
static unsigned int PSTARS_VAL;

static unsigned short pstar_e7,pstar_b1,pstar_ce;
static unsigned short pstar_ram[3];

static int Pstar_ba[0x1E]={
	0x02,0x00,0x00,0x01,0x00,0x03,0x00,0x00, //0
	0x02,0x00,0x06,0x00,0x22,0x04,0x00,0x03, //8
	0x00,0x00,0x06,0x00,0x20,0x07,0x00,0x03, //10
	0x00,0x21,0x01,0x00,0x00,0x63
};

static int Pstar_b0[0x10]={
	0x09,0x0A,0x0B,0x00,0x01,0x02,0x03,0x04,
	0x05,0x06,0x07,0x08,0x00,0x00,0x00,0x00
};

static int Pstar_ae[0x10]={
	0x5D,0x86,0x8C ,0x8B,0xE0,0x8B,0x62,0xAF,
	0xB6,0xAF,0x10A,0xAF,0x00,0x00,0x00,0x00
};

static int Pstar_a0[0x10]={
	0x02,0x03,0x04,0x05,0x06,0x01,0x0A,0x0B,
	0x0C,0x0D,0x0E,0x09,0x00,0x00,0x00,0x00,
};

static int Pstar_9d[0x10]={
	0x05,0x03,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

static int Pstar_90[0x10]={
	0x0C,0x10,0x0E,0x0C,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
static int Pstar_8c[0x23]={
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
	0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,
	0x02,0x02,0x03,0x03,0x03,0x04,0x04,0x04,
	0x03,0x03,0x03
};

static int Pstar_80[0x1a3]={
	0x03,0x03,0x04,0x04,0x04,0x04,0x05,0x05,
	0x05,0x05,0x06,0x06,0x03,0x03,0x04,0x04,
	0x05,0x05,0x05,0x05,0x06,0x06,0x07,0x07,
	0x03,0x03,0x04,0x04,0x05,0x05,0x05,0x05,
	0x06,0x06,0x07,0x07,0x06,0x06,0x06,0x06,
	0x06,0x06,0x06,0x07,0x07,0x07,0x07,0x07,
	0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x07,
	0x07,0x07,0x08,0x08,0x05,0x05,0x05,0x05,
	0x05,0x05,0x05,0x06,0x06,0x06,0x07,0x07,
	0x06,0x06,0x06,0x07,0x07,0x07,0x08,0x08,
	0x09,0x09,0x09,0x09,0x07,0x07,0x07,0x07,
	0x07,0x08,0x08,0x08,0x08,0x09,0x09,0x09,
	0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,
	0x08,0x08,0x09,0x09,0x05,0x05,0x06,0x06,
	0x06,0x07,0x07,0x08,0x08,0x08,0x08,0x09,
	0x07,0x07,0x07,0x07,0x07,0x08,0x08,0x08,
	0x08,0x09,0x09,0x09,0x06,0x06,0x07,0x03,
	0x07,0x06,0x07,0x07,0x08,0x07,0x05,0x04,
	0x03,0x03,0x04,0x04,0x05,0x05,0x06,0x06,
	0x06,0x06,0x06,0x06,0x03,0x04,0x04,0x04,
	0x04,0x05,0x05,0x06,0x06,0x06,0x06,0x07,
	0x04,0x04,0x05,0x05,0x06,0x06,0x06,0x06,
	0x06,0x07,0x07,0x08,0x05,0x05,0x06,0x07,
	0x07,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
	0x05,0x05,0x05,0x07,0x07,0x07,0x07,0x07,
	0x07,0x08,0x08,0x08,0x08,0x08,0x09,0x09,
	0x09,0x09,0x03,0x04,0x04,0x05,0x05,0x05,
	0x06,0x06,0x07,0x07,0x07,0x07,0x08,0x08,
	0x08,0x09,0x09,0x09,0x03,0x04,0x05,0x05,
	0x04,0x03,0x04,0x04,0x04,0x05,0x05,0x04,
	0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
	0x03,0x03,0x03,0x04,0x04,0x04,0x04,0x04,
	0x04,0x04,0x04,0x04,0x04,0x03,0x03,0x03,
	0x03,0x03,0x03,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
	0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00
};

static unsigned short PSTARS_protram_r(unsigned int offset)
{
	offset >>= 1;

	if (offset == 4)		//region
		return PgmInput[7];
	else if (offset >= 0x10)  //timer
	{
		return pstar_ram[offset-0x10]--;
	}
	return 0x0000;
}

static unsigned short PSTARS_r16(unsigned int offset)
{
	offset >>= 1;

	if(offset==0)
	{
		unsigned short d=PSTARS_VAL&0xffff;
		unsigned short realkey;
		realkey=PSTARSKEY>>8;
		realkey|=PSTARSKEY;
		d^=realkey;
		return d;
	}
	else if(offset==1)
	{
		unsigned short d=PSTARS_VAL>>16;
		unsigned short realkey;
		realkey=PSTARSKEY>>8;
		realkey|=PSTARSKEY;
		d^=realkey;
		return d;

	}
	return 0xff;
}

static void PSTARS_w16(unsigned int offset, unsigned short data)
{
	offset >>= 1;

	if(offset==0)
	{
		PSTARSINT[0]=data;
		return;
	}

	if(offset==1)
	{
		unsigned short realkey;
		if((data>>8)==0xff)
			PSTARSKEY=0xff00;
		realkey=PSTARSKEY>>8;
		realkey|=PSTARSKEY;
		{
			PSTARSKEY+=0x100;
			PSTARSKEY&=0xff00;
		 if(PSTARSKEY==0xff00)PSTARSKEY=0x100;
		 }
		data^=realkey;
		PSTARSINT[1]=data;
		PSTARSINT[0]^=realkey;

		switch(PSTARSINT[1]&0xff)
			{
				case 0x99:
				{
					PSTARSKEY=0x100;
					PSTARS_VAL=0x880000;

				}
				break;

				case 0xE0:
					{
						PSTARS_VAL=0xa00000+(PSTARSINT[0]<<6);
					}
					break;
				case 0xDC:
					{
						PSTARS_VAL=0xa00800+(PSTARSINT[0]<<6);
					}
					break;
				case 0xD0:
					{
						PSTARS_VAL=0xa01000+(PSTARSINT[0]<<5);
					}
					break;

				case 0xb1:
					{
						pstar_b1=PSTARSINT[0];
						PSTARS_VAL=0x890000;
					}
					break;
				case 0xbf:
					{
						PSTARS_VAL=pstar_b1*PSTARSINT[0];
					}
					break;

				case 0xc1: //TODO:TIMER  0,1,2,FIX TO 0 should be OK?
					{
						PSTARS_VAL=0;
					}
					break;
				case 0xce: //TODO:TIMER  0,1,2
					{
						pstar_ce=PSTARSINT[0];
						PSTARS_VAL=0x890000;
					}
					break;
				case 0xcf: //TODO:TIMER  0,1,2
					{
						pstar_ram[pstar_ce]=PSTARSINT[0];
						PSTARS_VAL=0x890000;
					}
					break;


				case 0xe7:
					{
						pstar_e7=(PSTARSINT[0]>>12)&0xf;
						PSTARS_REGS[pstar_e7]&=0xffff;
						PSTARS_REGS[pstar_e7]|=(PSTARSINT[0]&0xff)<<16;
						PSTARS_VAL=0x890000;
					}
					break;
				case 0xe5:
					{

						PSTARS_REGS[pstar_e7]&=0xff0000;
						PSTARS_REGS[pstar_e7]|=PSTARSINT[0];
						PSTARS_VAL=0x890000;
					}
					break;
				case 0xf8: //@73C
	   			{
	    			PSTARS_VAL=PSTARS_REGS[PSTARSINT[0]&0xf]&0xffffff;
	   			}
	   			break;


				case 0xba:
	   			{
	    			PSTARS_VAL=Pstar_ba[PSTARSINT[0]];
	   			}
	   			break;
				case 0xb0:
	   			{
	    			PSTARS_VAL=Pstar_b0[PSTARSINT[0]];
	   			}
	   			break;
				case 0xae:
	   			{
	    			PSTARS_VAL=Pstar_ae[PSTARSINT[0]];
	   			}
	   			break;
				case 0xa0:
	   			{
	    			PSTARS_VAL=Pstar_a0[PSTARSINT[0]];
	   			}
	   			break;
				case 0x9d:
	   			{
	    			PSTARS_VAL=Pstar_9d[PSTARSINT[0]];
	   			}
	   			break;
				case 0x90:
	   			{
	    			PSTARS_VAL=Pstar_90[PSTARSINT[0]];
	   			}
	   			break;
				case 0x8c:
	   			{
	    			PSTARS_VAL=Pstar_8c[PSTARSINT[0]];
	   			}
	   			break;
				case 0x80:
	   			{
	    			PSTARS_VAL=Pstar_80[PSTARSINT[0]];
	   			}
	   			break;
				default:
					 PSTARS_VAL=0x890000;
		}
	}
}

static void __fastcall pstars_write_byte(unsigned int address, unsigned char data)
{
	if ((address & 0xfffffc) == 0x500000) {
		PSTARS_w16(address & 3, data);
	}
}

static void __fastcall pstars_write_word(unsigned int address, unsigned short data)
{
	if ((address & 0xfffffc) == 0x500000) {
		PSTARS_w16(address & 3, data);
	}
}

static unsigned char __fastcall pstars_read_byte(unsigned int address)
{
	if ((address & 0xff0000) == 0x4f0000) {
		return PSTARS_protram_r(address & 0xffff);
	}

	if ((address & 0xfffffc) == 0x500000) {
		return PSTARS_r16(address & 3);
	}

	return 0;
}

static unsigned short __fastcall pstars_read_word(unsigned int address)
{
	if ((address & 0xff0000) == 0x4f0000) {
		return PSTARS_protram_r(address & 0xffff);
	}

	if ((address & 0xfffffc) == 0x500000) {
		return PSTARS_r16(address & 3);
	}

	return 0;
}

void reset_puzlstar()
{
	PSTARSKEY = 0;
	PSTARS_VAL = 0;
	PSTARSINT[0] = PSTARSINT[1] = 0;
	pstar_e7 = pstar_b1 = pstar_ce = 0;

	memset(PSTARS_REGS, 0, 16);
	memset(pstar_ram,   0,  3);
}

void install_protection_puzlstar()
{
	pPgmScanCallback = pstarsScan;

	SekOpen(0);
	SekMapHandler(4,	0x4f0000, 0x500003, SM_READ | SM_WRITE);

	SekSetReadWordHandler(4, pstars_read_word);
	SekSetReadByteHandler(4, pstars_read_byte);
	SekSetWriteWordHandler(4, pstars_write_word);
	SekSetWriteByteHandler(4, pstars_write_byte);
	SekClose();
}


//-----------------------------------------------------------------------------------------------------
// ASIC27A - Kov2, Martmast, etc

static unsigned char asic27a_to_arm = 0;
static unsigned char asic27a_to_68k = 0;

static inline void pgm_cpu_sync()
{
	int nCycles = SekTotalCycles() - Arm7TotalCycles();

	if (nCycles > 0) {
		Arm7Run(nCycles);
	}
}

static void __fastcall asic27A_write_byte(unsigned int /*address*/, unsigned char /*data*/)
{

}

static void __fastcall asic27A_write_word(unsigned int address, unsigned short data)
{
	if ((address & 0xfffffe) == 0xd10000) {
	//	pgm_cpu_sync();
		asic27a_to_arm = data & 0xff;
		Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_HOLD_LINE);
		return;
	}
}

static unsigned char __fastcall asic27A_read_byte(unsigned int address)
{
	if ((address & 0xff0000) == 0xd00000) {
		pgm_cpu_sync();
		return PGMARMShareRAM[(address & 0xffff)^1];
	}

	if ((address & 0xfffffc) == 0xd10000) {
		pgm_cpu_sync();
		return asic27a_to_68k;
	}

	return 0;
}

static unsigned short __fastcall asic27A_read_word(unsigned int address)
{
	if ((address & 0xff0000) == 0xd00000) {
		pgm_cpu_sync();
		return *((unsigned short*)(PGMARMShareRAM + (address & 0xfffe)));
	}

	if ((address & 0xfffffc) == 0xd10000) {
		pgm_cpu_sync();
		return asic27a_to_68k;
	}

	return 0;
}

static void asic27A_arm7_write_byte(unsigned int address, unsigned char data)
{
	switch (address)
	{
		case 0x38000000:
			asic27a_to_68k = data;
		return;
	}
}

static unsigned char asic27A_arm7_read_byte(unsigned int address)
{
	switch (address)
	{
		case 0x38000000:
			return asic27a_to_arm;
	}

	return 0;
}

void install_protection_asic27A()
{
	nPGMArm7Type = 2;
	pPgmScanCallback = asic27AScan;

	SekOpen(0);

	SekMapMemory(PGMARMShareRAM,	0xd00000, 0xd0ffff, SM_FETCH | SM_WRITE);

	SekMapHandler(4,		0xd00000, 0xd10003, SM_READ);
	SekMapHandler(5,		0xd10000, 0xd10003, SM_WRITE);

	SekSetReadWordHandler(4, asic27A_read_word);
	SekSetReadByteHandler(4, asic27A_read_byte);
	SekSetWriteWordHandler(5, asic27A_write_word);
	SekSetWriteByteHandler(5, asic27A_write_byte);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMUSER0,		0x08000000, 0x08000000+(nPGMExternalARMLen-1), ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM1,	0x18000000, 0x1800ffff, ARM7_RAM);
	Arm7MapMemory(PGMARMShareRAM,	0x48000000, 0x4800ffff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteByteHandler(asic27A_arm7_write_byte);
	Arm7SetReadByteHandler(asic27A_arm7_read_byte);
	Arm7Close();
}


//----------------------------------------------------------------------------------------------------------
// Kovsh/Photoy2k/Photoy2k2 asic27a emulation... (thanks to XingXing!)

static unsigned short kovsh_highlatch_arm_w = 0;
static unsigned short kovsh_lowlatch_arm_w = 0;
static unsigned short kovsh_highlatch_68k_w = 0;
static unsigned short kovsh_lowlatch_68k_w = 0;
static unsigned int kovsh_counter = 1;

static void __fastcall kovsh_asic27a_write_word(unsigned int address, unsigned short data)
{
	switch (address)
	{
		case 0x500000:
		case 0x600000:
			pgm_kov_asic27_w(0, data);
			kovsh_lowlatch_68k_w = data;
		return;

		case 0x500002:
		case 0x600002:
			pgm_kov_asic27_w(2, data);
			kovsh_highlatch_68k_w = data;
		return;
	}
}

static unsigned short __fastcall kovsh_asic27a_read_word(unsigned int address)
{
	if ((address & 0xffffc0) == 0x4f0000) {
		return *((unsigned short*)(PGMARMShareRAM + (address & 0x3e)));
	}

	switch (address)
	{
		case 0x500000:
		case 0x600000:
			pgm_cpu_sync();
			return kovsh_lowlatch_arm_w;

		case 0x500002:
		case 0x600002:
			pgm_cpu_sync();
			return kovsh_highlatch_arm_w;
	}

	return 0;
}

static void kovsh_asic27a_arm7_write_word(unsigned int address, unsigned short data)
{
	// written... but never read?
	if ((address & 0xffffff80) == 0x50800000) {
		*((unsigned short*)(PGMARMShareRAM + ((address>>1) & 0x3e))) = data;
		return;
	}
}

static void kovsh_asic27a_arm7_write_long(unsigned int address, unsigned int data)
{
	switch (address)
	{
		case 0x40000000:
		{
			kovsh_highlatch_arm_w = data >> 16;
			kovsh_lowlatch_arm_w = data;

			kovsh_highlatch_68k_w = 0;
			kovsh_lowlatch_68k_w = 0;
		}
		return;
	}
}

static unsigned int kovsh_asic27a_arm7_read_long(unsigned int address)
{
	switch (address)
	{
		case 0x40000000:
			return (kovsh_highlatch_68k_w << 16) | (kovsh_lowlatch_68k_w);

		case 0x4000000c:
			return kovsh_counter++;
	}

	return 0;
}

void install_protection_kovsh()
{
	nPGMArm7Type = 1;
	pPgmScanCallback = kovsh_asic27aScan;

	SekOpen(0);

	SekMapMemory(PGMARMShareRAM,	0x4f0000, 0x4f003f, SM_RAM);
	SekMapHandler(4,		0x500000, 0x600005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, kovsh_asic27a_read_word);
	SekSetWriteWordHandler(4, kovsh_asic27a_write_word);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteWordHandler(kovsh_asic27a_arm7_write_word);
	Arm7SetWriteLongHandler(kovsh_asic27a_arm7_write_long);
	Arm7SetReadLongHandler(kovsh_asic27a_arm7_read_long);
	Arm7Close();
}


//----------------------------------------------------------------------------------------------------------
// olds

static unsigned short olds_bs,olds_cmd3;

static unsigned int olds_prot_addr(unsigned short addr)
{
	unsigned int mode = addr&0xff;
	unsigned int offset = addr >> 8;
	unsigned int realaddr;

	switch (mode)
	{
		case 0:
		case 5:
		case 0xA:
			realaddr= 0x402A00+(offset<<2);
			break;

		case 2:
		case 8:
			realaddr= 0x402E00+(offset<<2);
			break;

		case 1:
			realaddr= 0x40307E;
			break;

		case 3:
			realaddr= 0x403090;
			break;

		case 4:
			realaddr= 0x40309A;
			break;

		case 6:
			realaddr= 0x4030A4;
			break;


		case 7:
			realaddr= 0x403000;
			break;

		case 9:
			realaddr= 0x40306E;
			break;

		default:
			realaddr= 0;
	}

	return realaddr;
}

static unsigned short olds_read_reg(unsigned short addr)
{
	int protaddr = (olds_prot_addr(addr)-0x400000)/2;
	return sharedprotram[protaddr]<<16|sharedprotram[protaddr+1];
}

static void olds_write_reg( unsigned short addr, unsigned int val)
{
	sharedprotram[(olds_prot_addr(addr)-0x400000)/2]=val>>16;
	sharedprotram[(olds_prot_addr(addr)-0x400000)/2+1]=val&0xffff;
}

static unsigned short olds_r16(unsigned int offset)
{
	offset = (offset >> 1) & 0x0f;

	unsigned short res = 0;

	if(offset == 1)
	{
		if(kb_cmd == 1)
			res = kb_reg&0x7f;

		if(kb_cmd == 2)
			res = olds_bs|0x80; // ok?

		if(kb_cmd == 3)
			res = olds_cmd3;

		else if(kb_cmd == 5)
		{
			unsigned int protvalue = 0x900000 | PgmInput[7]; // region from protection device.
			res = (protvalue>>(8*(kb_ptr-1))) & 0xff;
		}
	}

	return res;
}

static void olds_w16(unsigned int offset, unsigned short data)
{
	offset = (offset >> 1) & 0x0f;

	if(offset==0)
		kb_cmd=data;
	else
	{
		if(kb_cmd==0)
		{
			kb_reg=data;
		}
		else if(kb_cmd==2)
		{
			olds_bs = ((data & 3) << 6) | ((data & 4) << 3) | ((data & 8) << 1);
		}
		else if(kb_cmd==3)
		{
			unsigned short cmd=sharedprotram[0x3026/2];

			switch(cmd)
			{
				case 0x11:
				case 0x12:
					break;

				case 0x64:
					{
						unsigned short cmd0 = sharedprotram[0x3082/2];
						if((cmd0&0xff)==0x2) {
							unsigned short val0 = sharedprotram[0x3050/2];		//CMD_FORMAT
							olds_write_reg(val0,olds_read_reg(val0)+0x10000);
						}

						break;
					}

				default:
					break;
			}

			olds_cmd3=((data>>4)+1)&0x3;
		}
		else if(kb_cmd==4)
			kb_ptr=data;
		else if(kb_cmd==0x20)
		  kb_ptr++;
	}
}

static void __fastcall olds_write_word(unsigned int address, unsigned short data)
{
	olds_w16(address & 3, data);
}

static unsigned short __fastcall olds_read_word(unsigned int address)
{
	return olds_r16(address & 3);
}

static unsigned short __fastcall olds_mainram_read_word(unsigned int address)
{
	address &= 0x1fffe;

	unsigned short *pgm_mainram = (unsigned short*)PGM68KRAM;

	if(SekGetPC(-1)>=0x100000)
		pgm_mainram[0x178f4/2] = pgm_mainram[0x178D8/2];

	return pgm_mainram[address/2];
}

static unsigned char __fastcall olds_mainram_read_byte(unsigned int address)
{
	address &= 0x1ffff;

	return PGM68KRAM[address^1];
}

void reset_olds()
{
	kb_cmd = kb_reg = kb_ptr = olds_bs = olds_cmd3 = 0;
	memcpy(PGMUSER0, PGMUSER0 + 0x100000, 0x04000);
}

void install_protection_olds()
{
	pPgmScanCallback = oldsScan;

	sharedprotram = (unsigned short*)PGMUSER0;

	// load ram dump
	if (!strcmp(BurnDrvGetTextA(DRV_NAME), "olds100a")) {
		BurnLoadRom(PGMUSER0 + 0x100000, 15, 1);
	} else {
		BurnLoadRom(PGMUSER0 + 0x100000, 20, 1);
	}

	// stage fix
	unsigned short* dst = (unsigned short *)malloc(0x4000);
	if (dst) {
		memcpy(dst, PGMUSER0 + 0x100000, 0x4000);
		for (int i = 0; i < 0x4000 / 2; i++)
		{
			if (dst[i] == (0xffff-i))
				dst[i] = 0x4e75;
		}
		memcpy(PGMUSER0 + 0x100000, dst, 0x4000);
		free (dst);
	}

	reset_olds();

	SekOpen(0);

//	for (int i = 0; i < 0x040000; i += 0x4000) // mirrored 400000 - ?
//		SekMapMemory(PGMUSER0,	0x400000 + i, 0x403fff + i, SM_RAM);

	SekMapMemory(PGMUSER0,	0x400000, 0x403fff, SM_RAM);

	SekMapHandler(4,	0xdcb400, 0xdcb403, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, olds_read_word);
	SekSetWriteWordHandler(4, olds_write_word);

	// fix cheat
//	SekMapHandler(5,	0x800000, 0x8fffff, SM_READ | SM_FETCH);
	SekMapHandler(5,	0x8178f4, 0x8178f5, SM_READ | SM_FETCH);

	SekSetReadWordHandler(5, olds_mainram_read_word);
	SekSetReadByteHandler(5, olds_mainram_read_byte);

	SekClose();
}


//----------------------------------------------------------------------------------------------------------
// Ketsui, EspGaluda, DoDonPachi Dai-Ou-Jou

static unsigned short value0, value1, valuekey;
static unsigned int valueresponse;
static char ddp3internal_slot = 0;
static unsigned int ddp3slots[0x100];

static void __fastcall ddp3_asic_write(unsigned int offset, unsigned short data)
{
	switch (offset & 0x06)
	{
		case 0:
			value0 = data;
		return;

		case 2:
		{
			if ((data >> 8) == 0xff) valuekey = 0xffff;

			value1 = data ^ valuekey;
			value0 ^= valuekey;

			switch (value1 & 0xff)
			{
				case 0x40:
					valueresponse = 0x880000;
					ddp3slots[(value0 >> 10) & 0x1f] = (ddp3slots[(value0 >> 5) & 0x1f] + ddp3slots[(value0 >> 0) & 0x1f]) & 0xffffff;
				break;

				case 0x67:
					valueresponse = 0x880000;
					ddp3internal_slot = (value0 & 0xff00) >> 8;
					ddp3slots[ddp3internal_slot] = (value0 & 0x00ff) << 16;
				break;
		
				case 0xe5:
					valueresponse = 0x880000;
					ddp3slots[ddp3internal_slot] |= (value0 & 0xffff);
				break;
	
				case 0x8e:
					valueresponse = ddp3slots[value0 & 0xff];
				break;

				case 0x99: // reset?
					valuekey = 0;
					valueresponse = 0x880000;
				break;

				default:
					valueresponse = 0x880000;
				break;
			}

			valuekey = (valuekey + 0x0100) & 0xff00;
			if (valuekey == 0xff00) valuekey = 0x0100;
			valuekey |= valuekey >> 8;
		}
		return;

		case 4: return;
	}
}

static unsigned short __fastcall ddp3_asic_read(unsigned int offset)
{
	switch (offset & 0x02)
	{
		case 0: return (valueresponse >>  0) ^ valuekey;
		case 2: return (valueresponse >> 16) ^ valuekey;
	}

	return 0;
}

void reset_ddp3()
{
	value0 = 0;
	value1 = 0;
	valuekey = 0;
	valueresponse = 0;
	ddp3internal_slot = 0;

	memset (ddp3slots, 0, 0x100 * sizeof(int));
}

void install_protection_ket()
{
	pPgmScanCallback = ddp3Scan;

	SekOpen(0);
	SekMapHandler(4,		0x400000, 0x400005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	ddp3_asic_read);
	SekSetWriteWordHandler(4, 	ddp3_asic_write);
	SekClose();
}

void install_protection_ddp3()
{
	pPgmScanCallback = ddp3Scan;

	SekOpen(0);
	SekMapHandler(4,		0x500000, 0x500005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	ddp3_asic_read);
	SekSetWriteWordHandler(4, 	ddp3_asic_write);
	SekClose();
}


//-------------------------------------------------------------------------------------------
// S.V.G. - Spectral vs Generation / Demon Front / The Gladiator / The Killing Blade EX / Happy 6in1

static unsigned char svg_ram_sel = 0;
static unsigned char *svg_ram[2];

static void svg_set_ram_bank(int data)
{
	svg_ram_sel = data & 1;
	Arm7MapMemory(svg_ram[svg_ram_sel],	0x38000000, 0x3801ffff, ARM7_RAM);
	SekMapMemory(svg_ram[svg_ram_sel^1],	0x500000, 0x51ffff, SM_FETCH);
}

static void __fastcall svg_write_byte(unsigned int address, unsigned char data)
{
	pgm_cpu_sync();

	if ((address & 0xffe0000) == 0x0500000) {
		svg_ram[svg_ram_sel^1][(address & 0x1ffff)^1] = data;
		return;
	}

	switch (address)
	{
		case 0x5c0000:
		case 0x5c0001:
			Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_HOLD_LINE);
		return;
	}
}

static void __fastcall svg_write_word(unsigned int address, unsigned short data)
{
	pgm_cpu_sync();

	if ((address & 0xffe0000) == 0x0500000) {
		*((unsigned short*)(svg_ram[svg_ram_sel^1] + (address & 0x1fffe))) = data;
		
		return;
	}

	switch (address)
	{
		case 0x5c0000:
			Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_HOLD_LINE);
		return;

		case 0x5c0300:
			asic27a_to_arm = data;
		return;
	}
}

static unsigned char __fastcall svg_read_byte(unsigned int address)
{
	if ((address & 0xffe0000) == 0x0500000) {
		pgm_cpu_sync();

		int d = svg_ram[svg_ram_sel^1][(address & 0x1ffff)^1];
		return d;
	}

	switch (address)
	{
		case 0x5c0000:
		case 0x5c0001:
			return 0;
	}

	return 0;
}

static unsigned short __fastcall svg_read_word(unsigned int address)
{
	if ((address & 0xffe0000) == 0x0500000) {
		pgm_cpu_sync();

		return *((unsigned short*)(svg_ram[svg_ram_sel^1] + (address & 0x1fffe)));
	}

	switch (address)
	{
		case 0x5c0000:
		case 0x5c0001:
			return 0;

		case 0x5c0300:
			pgm_cpu_sync();
			return asic27a_to_68k;
	}

	return 0;
}

static void svg_arm7_write_byte(unsigned int address, unsigned char data)
{
	switch (address)
	{
		case 0x40000018:
			svg_set_ram_bank(data);
		return;

		case 0x48000000:
			asic27a_to_68k = data;
		return;
	}
}

static void svg_arm7_write_word(unsigned int /*address*/, unsigned short /*data*/)
{

}

static void svg_arm7_write_long(unsigned int address, unsigned int data)
{
	switch (address)
	{
		case 0x40000018:
			svg_set_ram_bank(data);
		return;

		case 0x48000000:
			asic27a_to_68k = data;
		return;
	}
}

static unsigned char svg_arm7_read_byte(unsigned int address)
{
	switch (address)
	{
		case 0x48000000:
		case 0x48000001:
		case 0x48000002:
		case 0x48000003:
			return asic27a_to_arm;
	}

	return 0;
}

static unsigned short svg_arm7_read_word(unsigned int address)
{
	switch (address)
	{
		case 0x48000000:
		case 0x48000002:
			return asic27a_to_arm;
	}

	return 0;
}

static unsigned int svg_arm7_read_long(unsigned int address)
{
	switch (address)
	{
		case 0x48000000:
			return asic27a_to_arm;
	}

	return 0;
}

void install_protection_svg()
{
	nPGMArm7Type = 3;

	pPgmScanCallback = svg_asic27aScan;

	svg_ram_sel = 0;
	svg_ram[0] = PGMARMShareRAM;
	svg_ram[1] = PGMARMShareRAM2;

	SekOpen(0);
	SekMapHandler(5,		0x500000, 0x5fffff, SM_RAM);
	SekSetReadWordHandler(5, 	svg_read_word);
	SekSetReadByteHandler(5, 	svg_read_byte);
	SekSetWriteWordHandler(5, 	svg_write_word);
	SekSetWriteByteHandler(5, 	svg_write_byte);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMUSER0,		0x08000000, 0x08000000 | (nPGMExternalARMLen-1), ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM1,	0x18000000, 0x1803ffff, ARM7_RAM);
	Arm7MapMemory(svg_ram[1],	0x38000000, 0x3801ffff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteByteHandler(svg_arm7_write_byte);
	Arm7SetWriteWordHandler(svg_arm7_write_word);
	Arm7SetWriteLongHandler(svg_arm7_write_long);
	Arm7SetReadByteHandler(svg_arm7_read_byte);
	Arm7SetReadWordHandler(svg_arm7_read_word);
	Arm7SetReadLongHandler(svg_arm7_read_long);
	Arm7Close();
}


//-----------------------------------------------------------------------------------------------------
// Save states

int kov_asic27Scan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= (unsigned char*)ASIC27REGS;
		ba.nLen		= 0x000000a * sizeof(short);
		ba.nAddress	= 0xff0000;
		ba.szName	= "Asic Registers";
		BurnAcb(&ba);

		ba.Data		= (unsigned char*)ASICPARAMS;
		ba.nLen		= 0x0000100 * sizeof(short);
		ba.nAddress	= 0xff1000;
		ba.szName	= "Asic Parameters";
		BurnAcb(&ba);

		ba.Data		= (unsigned char*)E0REGS;
		ba.nLen		= 0x0000010 * sizeof(short);
		ba.nAddress	= 0xff2000;
		ba.szName	= "Asic E0 Registers";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(ASIC27KEY);
		SCAN_VAR(ASIC27RCNT);
	}

	return 0;
}

int asic3Scan(int nAction, int *)
{
	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(asic3_reg);
		SCAN_VAR(asic3_latch[0]);
		SCAN_VAR(asic3_latch[1]);
		SCAN_VAR(asic3_latch[2]);
		SCAN_VAR(asic3_x);
		SCAN_VAR(asic3_y);
		SCAN_VAR(asic3_z);
		SCAN_VAR(asic3_h1);
		SCAN_VAR(asic3_h2);
		SCAN_VAR(asic3_hold);
	}

	return 0;
}

int killbldScan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMUSER0 + 0x000000;
		ba.nLen		= 0x0004000;
		ba.nAddress	= 0x300000;
		ba.szName	= "ProtRAM";
		BurnAcb(&ba);

		ba.Data		= (unsigned char*)kb_regs;
		ba.nLen		= 0x00100 * sizeof(int);
		ba.nAddress	= 0xfffffc00;
		ba.szName	= "Protection Registers";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(kb_cmd);
		SCAN_VAR(kb_reg);
		SCAN_VAR(kb_ptr);
		SCAN_VAR(kb_region_sequence_position);
	}

	return 0;
}

int drgw3Scan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMUSER0 + 0x000000;
		ba.nLen		= 0x0004000;
		ba.nAddress	= 0x300000;
		ba.szName	= "ProtRAM";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(kb_cmd);
		SCAN_VAR(kb_reg);
		SCAN_VAR(kb_ptr);
		SCAN_VAR(dw3_swap);
	}

	return 0;
}

int pstarsScan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= (unsigned char*)PSTARSINT;
		ba.nLen		= 0x0000002 * sizeof(short);
		ba.nAddress	= 0xff0000;
		ba.szName	= "Asic Written Values";
		BurnAcb(&ba);

		ba.Data		= (unsigned char*)PSTARS_REGS;
		ba.nLen		= 0x0000010 * sizeof(int);
		ba.nAddress	= 0xff1000;
		ba.szName	= "Asic Register";
		BurnAcb(&ba);

		ba.Data		= (unsigned char*)pstar_ram;
		ba.nLen		= 0x0000003 * sizeof(short);
		ba.nAddress	= 0xff2000;
		ba.szName	= "Asic RAM";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(PSTARSKEY);
		SCAN_VAR(PSTARS_VAL);
		SCAN_VAR(pstar_e7);
		SCAN_VAR(pstar_b1);
		SCAN_VAR(pstar_ce);
	}

	return 0;
}

int asic27AScan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMARMShareRAM;
		ba.nLen		= 0x0010000;
		ba.nAddress	= 0xd00000;
		ba.szName	= "ARM SHARE RAM";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM0;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 0";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM1;
		ba.nLen		= 0x0010000;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 1";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM2;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 2";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		Arm7Scan(nAction);

		SCAN_VAR(asic27a_to_arm);
		SCAN_VAR(asic27a_to_68k);
	}

 	return 0;
}

int oldsScan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMUSER0 + 0x000000;
		ba.nLen		= 0x0004000;
		ba.nAddress	= 0x400000;
		ba.szName	= "ProtRAM";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(kb_cmd);
		SCAN_VAR(kb_reg);
		SCAN_VAR(kb_ptr);
		SCAN_VAR(olds_bs);
		SCAN_VAR(olds_cmd3);
	}

	return 0;
}

int kovsh_asic27aScan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMARMShareRAM;
		ba.nLen		= 0x0000040;
		ba.nAddress	= 0x400000;
		ba.szName	= "ARM SHARE RAM";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM0;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 0";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM2;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 1";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		Arm7Scan(nAction);

		SCAN_VAR(kovsh_highlatch_arm_w);
		SCAN_VAR(kovsh_lowlatch_arm_w);
		SCAN_VAR(kovsh_highlatch_68k_w);
		SCAN_VAR(kovsh_lowlatch_68k_w);
		SCAN_VAR(kovsh_counter);
	}

 	return 0;
}

int svg_asic27aScan(int nAction,int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMARMShareRAM;
		ba.nLen		= 0x0020000;
		ba.nAddress	= 0x400000;
		ba.szName	= "ARM SHARE RAM #0 (address 500000)";
		BurnAcb(&ba);

		ba.Data		= PGMARMShareRAM2;
		ba.nLen		= 0x0020000;
		ba.nAddress	= 0x500000;
		ba.szName	= "ARM SHARE RAM #1";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM0;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 0";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM1;
		ba.nLen		= 0x0040000;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 1";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM2;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 2";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		Arm7Scan(nAction);
		SCAN_VAR(asic27a_to_arm);
		SCAN_VAR(asic27a_to_68k);

		SCAN_VAR(svg_ram_sel);
		svg_set_ram_bank(svg_ram_sel);
	}

 	return 0;
}

int ddp3Scan(int nAction, int *)
{
	struct BurnArea ba;

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= (unsigned char*)ddp3slots;
		ba.nLen		= 0x0000100 * sizeof(int);
		ba.nAddress	= 0xff00000;
		ba.szName	= "ProtRAM";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(value0);
		SCAN_VAR(value1);
		SCAN_VAR(valuekey);
		SCAN_VAR(valueresponse);
		SCAN_VAR(ddp3internal_slot);
	}

	return 0;
}


//-------------------------------------------------------------------------------------------
// ddp2 - preliminary (kludgy)

static int ddp2_asic27_0xd10000 = 0;

static void __fastcall Ddp2WriteByte(unsigned int address, unsigned char data)
{
	if ((address & 0xffe000) == 0xd00000) {
		PGMUSER0[(address & 0x1fff)^1] = data;
		*((unsigned short*)(PGMUSER0 + 0x0010)) = 0;
		*((unsigned short*)(PGMUSER0 + 0x0020)) = 1;
		return;
	}

	if ((address & 0xffffffe) == 0xd10000) {
		ddp2_asic27_0xd10000=data;
		return;
	}
}

static void __fastcall Ddp2WriteWord(unsigned int address, unsigned short data)
{
	if ((address & 0xffe000) == 0xd00000) {
		*((unsigned short*)(PGMUSER0 + (address & 0x1ffe))) = data;
		*((unsigned short*)(PGMUSER0 + 0x0010)) = 0;
		*((unsigned short*)(PGMUSER0 + 0x0020)) = 1;
		return;
	}

	if ((address & 0xffffffe) == 0xd10000) {
		ddp2_asic27_0xd10000=data;
		return;
	}
}

static unsigned char __fastcall Ddp2ReadByte(unsigned int address)
{
	if ((address & 0xfffffe) == 0xd10000) {
		ddp2_asic27_0xd10000++;
		ddp2_asic27_0xd10000&=0x7f;
		return ddp2_asic27_0xd10000;
	}

	if ((address & 0xffe000) == 0xd00000) {
		*((unsigned short*)(PGMUSER0 + 0x0002)) = PgmInput[7]; // region
		*((unsigned short*)(PGMUSER0 + 0x1f00)) = 0;
		return PGMUSER0[(address & 0x1fff)^1];
	}

	return 0;
}

static unsigned short __fastcall Ddp2ReadWord(unsigned int address)
{
	if ((address & 0xfffffe) == 0xd10000) {
		ddp2_asic27_0xd10000++;
		ddp2_asic27_0xd10000&=0x7f;
		return ddp2_asic27_0xd10000;
	}

	if ((address & 0xffe000) == 0xd00000) {
		*((unsigned short*)(PGMUSER0 + 0x0002)) = PgmInput[7]; // region
		*((unsigned short*)(PGMUSER0 + 0x1f00)) = 0;
      		return *((unsigned short*)(PGMUSER0 + (address & 0x1ffe)));
	}

	return 0;
}

void install_protection_ddp2()
{
	memset (PGMUSER0, 0, 0x2000);

	SekOpen(0);
	SekMapHandler(4,             0xd00000, 0xd1ffff, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4,    Ddp2ReadWord);
	SekSetReadByteHandler(4,    Ddp2ReadByte);
	SekSetWriteWordHandler(4,    Ddp2WriteWord);
	SekSetWriteByteHandler(4,    Ddp2WriteByte);
	SekClose();
}
